package org.hawk.gwt.ppc.scanner.utils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/**
 * Represents {@link JavaTypeScope} derived from already loaded
 * class. All public methods of {@link JavaTypeScope} will be 
 * overridden by corresponding {@link Class} methods.
 * @author alex.bereznevatiy@gmail.com
 */
public class JavaClassScope extends JavaTypeScope {
	private Class<?> clazz;

	JavaClassScope(Class<?> clazz) {
		super(null, 0, new LinkedList<JavaVariable>(), null);
		this.clazz = clazz;
		setClassName(clazz.getName());
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaTypeScope#getVariables(int)
	 */
	@Override
	public List<JavaVariable> getVariables(int index) {
		List<JavaVariable> result = new LinkedList<JavaVariable>();
		for(Field fld : clazz.getFields()){
			result.add(getVariableFromReflect(fld));
		}
		return Collections.unmodifiableList(result);
	}
	
	private JavaVariable getVariableFromReflect(Field fld) {
		return new JavaVariable(fld.getName(), fld.getType().getName(), 0, this);
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaTypeScope#getVariable(int, java.lang.String)
	 */
	@Override
	public JavaVariable getVariable(int index, String name) {
		try{
			return getVariableFromReflect(clazz.getField(name));
		}
		catch(NoSuchFieldException ex){
			return ObjectTypeScope.INSTANCE.getVariable(index, name);
		}
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaTypeScope#getMethods()
	 */
	@Override
	public List<JavaMethod> getMethods() {
		List<JavaMethod> result = new LinkedList<JavaMethod>();
		for(Method fld : clazz.getMethods()){
			result.add(getMethodFromReflect(fld));
		}
		return Collections.unmodifiableList(result);
	}

	private JavaMethod getMethodFromReflect(Method m) {
		List<JavaVariable> params = new LinkedList<JavaVariable>();
		int i=0;
		for(Class<?> cls : m.getParameterTypes()){
			params.add(new JavaVariable("arg"+i, cls.getName(),0, this));
			i++;
		}
		return new JavaMethod(m.getName(), this, 0, 
			params, m.getReturnType().getName(),m.isVarArgs());
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaScope#getParent()
	 */
	@Override
	public JavaScope getParent() {
		return null;
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaScope#getScope(int)
	 */
	@Override
	public JavaScope getScope(int index) {
		return this;
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaScope#getChildren()
	 */
	@Override
	public List<JavaScope> getChildren() {
		return Collections.emptyList();
	}
	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if(this==obj)return true;
		if(obj instanceof JavaClassScope){
			return ((JavaClassScope)obj).clazz.equals(clazz);
		}
		return false;
	}
	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return clazz.toString();
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaTypeScope#getImplementedInterfaces()
	 */
	@Override
	public List<JavaTypeScope> getImplementedInterfaces() {
		List<JavaTypeScope> result = new LinkedList<JavaTypeScope>();
		for(Class<?> iface : clazz.getInterfaces()){
			result.add(new JavaClassScope(iface));
		}
		return Collections.unmodifiableList(result);
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaTypeScope#getDeclaredConstructors()
	 */
	@Override
	public List<JavaConstructor> getDeclaredConstructors() {
		List<JavaConstructor> result = new LinkedList<JavaConstructor>();
		for(Constructor<?> constructor : clazz.getDeclaredConstructors()){
			JavaConstructor c = new JavaConstructor(clazz.getName(), this, -1, new ArrayList<JavaVariable>(), constructor.isVarArgs());
			for (Class<?> param : constructor.getParameterTypes()) {
				c.addParameter(param.getName());
			}
			result.add(c);
		}
		return Collections.unmodifiableList(result);
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaTypeScope#getEntityType()
	 */
	@Override
	public String getEntityType() {
		if(isInterface())return JavaClassDescriptor.INTERFACE_KEYWORD;
		if(isEnum())return JavaClassDescriptor.ENUM_KEYWORD;
		if(isAnnotation())return JavaClassDescriptor.ANNOTATION_KEYWORD_START;
		return JavaClassDescriptor.CLASS_KEYWORD;
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaTypeScope#isInterface()
	 */
	@Override
	public boolean isInterface() {
		return clazz.isInterface();
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaTypeScope#isClass()
	 */
	@Override
	public boolean isClass() {
		return !clazz.isEnum()&&
			!clazz.isInterface()&&
			!clazz.isAnnotation();
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaTypeScope#isEnum()
	 */
	@Override
	public boolean isEnum() {
		return clazz.isEnum();
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaTypeScope#isAnnotation()
	 */
	@Override
	public boolean isAnnotation() {
		return clazz.isAnnotation();
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaTypeScope#getClassName()
	 */
	@Override
	public String getClassName() {
		return clazz.getCanonicalName();
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaTypeScope#getResolvedClassName()
	 */
	@Override
	public String getResolvedClassName() {
		return getClassName();
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaTypeScope#getSuperClass()
	 */
	@Override
	public JavaTypeScope getSuperClass() {
		Class<?> cls = clazz.getSuperclass();
		if (cls == null) {
			return null;
		}
		if (cls.getSuperclass() == null) {
			return ObjectTypeScope.INSTANCE;
		}
		return new JavaClassScope(cls);
	}
	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.scanner.utils.JavaTypeScope#getGenericSuperclass()
	 */
	@Override
	public JavaGenericTypeScope getGenericSuperclass() {
		Type superclass = clazz.getGenericSuperclass();
		if (superclass instanceof Class) {
			return null;
		}
		return new JavaParameterizedGenericTypeScope(superclass, getOwner());
	}
	@Override
	public boolean isAnonymous() {
		return clazz.isAnonymousClass() || clazz.isLocalClass();
	}

}
